Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement lowest fee metric correctly #13

Merged
merged 7 commits into from
Jan 15, 2024

Conversation

LLFourn
Copy link
Collaborator

@LLFourn LLFourn commented Dec 19, 2023

This replaces #11. This first commit just fixes the metric to make the tests fail. Note the previous calculation was overthinking it. The fee metric is just inputs - outputs + long_term_feerate * change_weight.

Next steps:

  1. Make ci actually run the tests and get them to fail.
  2. Fix the metric lower bound

@LLFourn LLFourn force-pushed the fix-lowest-fee branch 3 times, most recently from 42f85c7 to cb70133 Compare December 21, 2023 07:55
@LLFourn LLFourn changed the title Make lowest fee test fail by implementing score correctly Implement lowest fee metric correctly Dec 22, 2023
if is_target_met true it should continue to be true if you add a drain
from a ChangePolicy. We should try and guarantee we never add a drain
when it would invalidate the selection.
@LLFourn LLFourn force-pushed the fix-lowest-fee branch 2 times, most recently from 38229e5 to dc46db3 Compare December 22, 2023 03:43
It may improve best score allowing us to avoid ever adding the children
to the heap if their bound function doesn't think they can improve on it.
That proptest is not hitting for some reason.
@LLFourn
Copy link
Collaborator Author

LLFourn commented Dec 22, 2023

This now includes a fix for the lowest fee metric. The bound function uses the simplification from #14 to precisely target the amount of negative effective value it could use to remove the change output which is the position of the possible lowest fee. This is where most of complexity of the code comes from and unfortunately the logic of this branch isn't being hit by the proptests. I tried playing around with the parameters of proptest and running a lot more times to hit it but it refused. I had to write a manual test to target this branch.

Also I made a change to branch and bound in 17cc8f2

We now score the branch before bounding its children. This allows the parent branch to improve the best score which may exclude its children (this leads to less iterations to find the best solution in one of the tests).

@LLFourn LLFourn requested a review from evanlinjin December 22, 2023 04:22
If we only add a drain when it wouldn't increase waste then it is
impossible to decrease waste by adding a new input -- but the logic of
the bound function has to account for this situation. So by using
min_value_and_waste we never test it!
@LLFourn
Copy link
Collaborator Author

LLFourn commented Dec 22, 2023

This is where most of complexity of the code comes from and unfortunately the logic of this branch isn't being hit by the proptests. I tried playing around with the parameters of proptest and running a lot more times to hit it but it refused.

I figured out why the tests weren't hitting it and fixed that: 9e1cecd

If we only add a drain when it wouldn't increase waste then it is
impossible to decrease waste by adding a new input -- but the logic of
the bound function has to account for this situation. So by using
min_value_and_waste we never test it!

Copy link
Member

@evanlinjin evanlinjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this forward and fixing my screw-up. I'm liking how this is looking.

Most of my comments are nits, except for the change to the assert! statement in lowest_fee.rs - please have a second look at it!

README.md Outdated Show resolved Hide resolved
Comment on lines +54 to +69
let mut return_val = None;
if !branch.is_exclusion {
if let Some(score) = self.metric.score(&selector) {
let better = match self.best {
Some(best_score) => score < best_score,
None => true,
};
if better {
self.best = Some(score);
return_val = Some(score);
}
};
}

let score = match self.metric.score(&selector) {
Some(score) => score,
None => return Some(None),
};

if let Some(best_score) = &self.best {
if score >= *best_score {
return Some(None);
}
}
self.best = Some(score);
Some(Some((selector, score)))
self.insert_new_branches(&selector);
Some(return_val.map(|score| (selector, score)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I prefer the way it was written before as it was more readable for me.

@@ -67,7 +67,7 @@ impl BnbMetric for LowestFee {

if let Some((_, low_sats_per_wu_candidate)) = cs.unselected().next_back() {
let ev = low_sats_per_wu_candidate.effective_value(self.target.feerate);
if ev < 0.0 {
if ev < -0.0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Would this make a difference? In the f32 docs, it states that 0.0 == -0.0 when it comes to comparisons.

If it does make a difference, please have a comment as it is not immediately apparent.

let mut cs = cs.clone();
cs.select(0);
cs.select(2);
cs.excess(target, Drain::none());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: do we need this? Was the intention to print the best solution's excess to check that it is greater than 0 for the test?

src/coin_selector.rs Outdated Show resolved Hide resolved
src/metrics/lowest_fee.rs Show resolved Hide resolved
tests/lowest_fee.rs Show resolved Hide resolved
Copy link
Member

@evanlinjin evanlinjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 6ae0fdf

@evanlinjin evanlinjin merged commit 4023034 into bitcoindevkit:master Jan 15, 2024
5 checks passed
@evanlinjin evanlinjin mentioned this pull request Jan 15, 2024
evanlinjin added a commit that referenced this pull request Jan 15, 2024
f991504 chore: bump to v0.2 (志宇)

Pull request description:

  # Changelog

  * Fix lowest fee metric (#13)
  * Simplify change policy (#14)

Top commit has no ACKs.

Tree-SHA512: a92bbc99ef5c1ab06fdb019916e3c88970ce989a7dd36471d486f4601b2ecc439a0787432994e9b14f0f8fbdfb031c3e5db9c5723068245db1b5f981cfc9f1e9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants